package com.jcsz.gulimall.auth.web;

import com.alibaba.fastjson.TypeReference;
import com.jcsz.common.constant.AuthServerConstant;
import com.jcsz.common.exception.BizCodeEnume;
import com.jcsz.common.utils.R;
import com.jcsz.common.vo.MemberRespVo;
import com.jcsz.gulimall.auth.feign.MemberFeignService;
import com.jcsz.gulimall.auth.feign.ThirdFeignService;
import com.jcsz.gulimall.auth.vo.UserLoginVo;
import com.jcsz.gulimall.auth.vo.UserRegistVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @className: LoginController
 * @author: 一把Po吉他
 * @date: 2022/11/7 22:10
 * @version: 1.0
 **/
@Controller
public class LoginController {

    @Autowired
    private ThirdFeignService thirdFeignService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private MemberFeignService memberFeignService;

    @GetMapping("/sms/sendCode")
    @ResponseBody
    public R sendCode(@RequestParam("phone") String phone) {
        /**
         * 1、接口防刷，防止同一个phone在60秒内重复再次发送验证码
         * 2、验证码的再次校验，存redis，因为这个验证码不是永远存储的
         * 2.1、存入redis，key是一定包含手机号的，值是验证码
         * 2.3、如果是60秒以后，那么在发送新的验证码
         *
         * 验证码发送频率过多的解决方案：
         * 1、保存验证码的时候，再保存当前验证码设置的系统时间。
         *      1.1、只要你想要发送验证码，先去看下redis中有没有
         *      1.2、如果有了，再去看下这个时间，如果这个时间再60秒内，那就60秒以后再发送
         */
        // 1、先从redis中查询是否存在验证码
        String redisCode = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone);
        // 2、如果验证码不为空，则验证该验证码是否60秒内
        if (!StringUtils.isEmpty(redisCode)) {
            // 3、验证短信发送时间是否在60秒内
            long saveCodeTime = Long.parseLong(redisCode.split("_")[1]);// value = code_当前时间加毫秒数
            // 3.1 如果当前时间减去验证码存入redis的时间小于60秒 ，说明用户多次获取验证码
            if (System.currentTimeMillis() - saveCodeTime < 60000) {
                return R.error(BizCodeEnume.SMS_CODE_EXCEPTION.getCode(), BizCodeEnume.SMS_CODE_EXCEPTION.getMsg());
            }

        }
        // 4、生成验证码 使用UUID生成会有英文，对用户不友好
        // String code = UUID.randomUUID().toString().substring(0,5).toUpperCase();
        int randomCode = (int) (Math.random() * 100000); // 生成五位随机数字
        String code = String.valueOf(randomCode);
        // 5、拼装值 验证码加上当前时间毫秒
        String saveCode = code + "_" + System.currentTimeMillis();
        // 6、存入redis k->指定前缀加上手机号 value-> 拼装值  time-> 5分钟过期时间，防止用户发送验证码
        redisTemplate.opsForValue().set(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone, saveCode, 20, TimeUnit.MINUTES);
        // 7、调用第三方服务根据手机发送验证码
        thirdFeignService.sendCode(phone, code);
        return R.ok();
    }

    @PostMapping("/regist")
    public String regist(@Valid UserRegistVo registVo, BindingResult result, RedirectAttributes redirectAttributes) {
        // 1、如果校验出错，那么说明参数有问题
        if (result.hasErrors()) {
            // 1.1、将错误结果转成map key为受影响的字段 value为错误结果
            Map<String, String> errorMap = result.getFieldErrors().stream().collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
            // 1.2、将错误放到 redirect中,存放错误信息，让前端感知并获取错误信息
            redirectAttributes.addFlashAttribute("errors", errorMap);
            return "redirect:http://auth.gulimall.com/reg.html";
        }
        // 2、验证码是否相同
        String code = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + registVo.getPhone());
        if (!StringUtils.isEmpty(code)) {
            // 3、redis里面存了这个手机号对应的验证码,对结果进行截串
            String saveCode = code.split("_")[0];
            // 验证码不通过
            if (!saveCode.equals(registVo.getCode())) {
                Map<String, String> errorMap = new HashMap<>();
                // 存放错误信息，让前端感知并获取错误信息
                errorMap.put("code", "验证码错误.");
                redirectAttributes.addFlashAttribute("errors", errorMap);
                return "redirect:http://auth.gulimall.com/reg.html";// 注册页
            } else {
                //验证正确
                redisTemplate.delete(AuthServerConstant.SMS_CODE_CACHE_PREFIX + registVo.getPhone());
                R r = memberFeignService.regist(registVo);
                if (r.getCode() != 200) {
                    Map<String, String> errorMap = new HashMap<>();
                    // 存放错误信息，让前端感知并获取错误信息
                    errorMap.put("msg", r.get("msg", new TypeReference<String>() {
                    }));
                    redirectAttributes.addFlashAttribute("errors", errorMap);
                    return "redirect:http://auth.gulimall.com/reg.html";// 注册页
                } else {
                    // 远程调用注册服务成功，跳转到登录页面
                    return "redirect:http://auth.gulimall.com/login.html";
                }


            }
        } else {
            // redis存验证码过期
            Map<String, String> errorMap = new HashMap<>();
            // 存放错误信息，让前端感知并获取错误信息
            errorMap.put("code", "验证码错误.");
            redirectAttributes.addFlashAttribute("errors", errorMap);
            return "redirect:http://auth.gulimall.com/reg.html";// 注册页
        }

    }

    @PostMapping("/login")
    public String login(UserLoginVo userLoginVo, HttpSession session, RedirectAttributes redirectAttributes) {
        R r = memberFeignService.login(userLoginVo);
        if (r.getCode() != 200) {
            // 远程服务请求失败
            Map<String, String> errorMap = new HashMap<>();
            errorMap.put("msg", r.get("msg", new TypeReference<String>() {
            }));
            // 响应错误信息给到页面
            redirectAttributes.addFlashAttribute("errors", errorMap);
            // 重定向到登陆页面
            return "redirect:http://auth.gulimall.com/login.html";
        } else {
            // 远程服务请求成功
            MemberRespVo loginVo = r.get("data", new TypeReference<MemberRespVo>() {
            });
            System.out.println(loginVo);
            session.setAttribute(AuthServerConstant.LOGIN_USER, loginVo);
            return "redirect:http://gulimall.com";
        }
    }

    @GetMapping("/login.html")
    public String loginPage(HttpSession session) {
        Object attribute = session.getAttribute(AuthServerConstant.LOGIN_USER);
        // 登陆了
        if (attribute != null) {
            return "redirect:http://gulimall.com";
        } else {
            // 1、用户没有登陆
            return "login";
        }
    }
}
